#include <math.h>
#include "sistemaslineares.h"

Vector::Vector(void) {
	this->set(0,0,0);
}

Vector::Vector(double x, double y, double z) {
	this->set(x,y,z);
}

Vector::Vector(Vector *b){
	this->set(b->getCoord(0) , b->getCoord(1), b->getCoord(2));
}

Vector::~Vector() {
	//
}
/**
* o produto escalar de dois vetores e o mesmo que o produto interno 
* questao de nomeacao 
* o produto por um escalar esta implementado em multiplicacao()
*/
double Vector::produtoInterno (Vector *b) {
	return (this->x * b->x) + (this->y * b->y) + (this->z * b->z);
}

double Vector::modulo () {
	return sqrt(pow(this->x, 2) + pow(this->y, 2) + pow(this->z, 2));
}

void Vector::set(double x, double y, double z) {
	this->x = x;
	this->y = y;
	this->z = z;
}

void Vector::setCoord(double coord, int index){
	switch(index){
		case 0:
			this->x = coord;
			break;
		case 1:
			this->y = coord;
			break;
		case 2:
			this->z = coord;
			break;
		defaults:
			break;
	}
}


double Vector::getCoord(int index){
	switch(index){
		case 0:
			return this->x;
			break;
		case 1:
			return this->y;
			break;
		case 2:
			return this->z;
			break;
		defaults:
			return 0;
	}
}

Vector* Vector::vetorUnitario () {
	Vector *v = new Vector();
	double mod = this->modulo();
	v->x = this->x / mod;
	v->y = this->y / mod;
	v->z = this->z / mod;

	return v;
}

Vector* Vector::raioRefletido (Vector *normal ) {
	Vector *vProj = projecao(normal);
	Vector *vIncrH = vProj->subtracao(this);
	Vector *vReflect = vProj->soma(vIncrH);

	return vReflect;
}

Vector* Vector::projecao(Vector *ontoVector ) {
	Vector *vProjectVector = new Vector();

	double fNewScale = this->produtoInterno(ontoVector) / ontoVector->produtoInterno(ontoVector);
	vProjectVector = ontoVector->multiplicacao(fNewScale);

	return vProjectVector;
}


Vector* Vector::combinacaoLinear(Vector *vetor) 
{
	/*Vector *result = new Vector(0,0,0);
	//setVetor (&result, 0, 0, 0);
	//va_list arglist;

	//va_start( arglist, count );

	Vector *vetor;
	double scalar;

	while( count-- ) {
		scalar = va_arg( arglist, double );
		vetor = va_arg( arglist, *Vector );

		result->x += ( scalar * vetor->x );
		result->y += ( scalar * vetor->y );
		result->z += ( scalar * vetor->z );
	}
	//va_end( arglist );*/
	return this->soma(vetor);
	//return result;
}

Vector* Vector::combinacaoLinear(Vector *vetor, Vector *a, Vector *b){
	Vector *result = new Vector();
	for(int i=0; i<3; i++){
		result->setCoord( (this->getCoord(i)*a->getCoord(i) + vetor->getCoord(i)*b->getCoord(i)), i);
	}
	return result;
}

double Vector::produtoEscalar(Vector *b) {
	return (this->getCoord(0)*b->getCoord(0) + this->getCoord(1)*b->getCoord(1) + this->getCoord(2)*b->getCoord(2));
}

Vector* Vector::produtoVetorial(Vector *v2 ) 
{
	Vector *v = new Vector();
	v->set(
			this->y * v2->z - this->z * v2->y, 
			this->z * v2->x - this->x * v2->z, 
			this->x * v2->y - this->y * v2->x
	);
	return v;
}


double Vector::calculaAngulo(Vector *b){
	return acos((this->produtoEscalar(b)) / (this->modulo() * b->modulo()));
}

bool Vector::ortogonal(Vector *v){
	if (this->produtoEscalar(v) == 0){
		return true;
	} else {
		return false;
	}
}

bool Vector::linearmenteIndependente(Vector *v){
	double n[3];
	for( int i=0; i<3; i++){
		if (v->getCoord(i) != 0){
			n[i] = this->getCoord(i) / v->getCoord(i);
		} else {
			if(this->getCoord(i) == 0){ // os dois tem coordenadas 0.0
				n[i] = 1;
			} else { // como um tem coordenada 0.0 e o outro nao , nao podem ser combinacao linear
				return false;
			}
		}
	}

	if((n[0] == n[1]) && (n[1] == n[2])) {
		return true;
	} else {
		return false;
	}
}

bool Vector::linearmenteIndependente(Vector *v1, Vector *v2, Vector *v3){
	Matrix3D *r = new Matrix3D();
	for(int i=0; i<3; i++){
		r->setCoord( v1->getCoord(i), i, 0);
		r->setCoord( v2->getCoord(i), i, 1);
		r->setCoord( v3->getCoord(i), i, 2);
	}
	if( r->determinante() == 0.0){
		return true;
	} else {
		return false;
	}
}

Matrix3D* Vector::matrixMudancaBase(Matrix3D *baseOrigem, Matrix3D *baseDestino){
	Matrix3D *result = new Matrix3D();
	Vector *b = new Vector();
	Vector *x = new Vector();
	// repete o mesmo processo para os tres vetores da base origem 
	for(int i=0; i<3; i++){
		// preenche o vetor b com as coordenadas do vetor base
		for(int j=0; j<3; j++){
			b->setCoord( baseOrigem->getCoord( j, i), j);
		}
		// resolve o sistema b = baseDestino * x (obtendo x)
		SistemasLineares::SolucaoPorDeterminante( baseDestino, b, x);
		// preenche a linha com o vetor solucao x
		for(int j=0; j<3; j++){
			result->setCoord( x->getCoord(j), j, i);
		}		
	}
	
	return result;
}


Vector* Vector::soma(Vector *b)  {
	Vector *retorno = new Vector();
	retorno->x = this->x + b->x;
	retorno->y = this->y + b->y;
	retorno->z = this->z + b->z;

	return retorno;
}

Vector* Vector::subtracao(Vector *b) {
	Vector *retorno = new Vector();
	retorno->x = this->x - b->x;
	retorno->y = this->y - b->y;
	retorno->z = this->z - b->z;

	return retorno;
}

Vector* Vector::multiplicacao(double escalar){
	Vector *retorno = new Vector();
	retorno->x = this->x * escalar;
	retorno->y = this->y * escalar;
	retorno->z = this->z * escalar;

	return retorno;

}

Vector* Vector::multiplicacao(Matrix3D *m){
	Vector* result = new Vector();
	for(int i=0; i<3; i++){
		double soma = 0;
		for(int j=0; j<3; j++){
			soma = soma + m->getCoord( i, j) * this->getCoord(j);
		}
		result->setCoord( (soma), i);
	}
	return result;
}

void Vector::print() {
	cout << "x = "<< this->x << " y = " << this->y << " z = " << this->z << "\n";
}

// monta a matrix de rotacao em torno do vetor v
Matrix3D* Vector::rotacao( Vector *u, double a){
	
	double x, y, z;

	x = u->getCoord(0);
	y = u->getCoord(1);
	z = u->getCoord(2);

	/*      |  0 -z  y  |
	*  S =  |  z  0 -x  |
	*       | -y  x  0  | 
	*/
	Matrix3D *s = new Matrix3D();
	s->setCoord(  0, 0, 0);
	s->setCoord( -z, 0, 1);
	s->setCoord(  y, 0, 2);

	s->setCoord(  z, 1, 0);
	s->setCoord(  0, 1, 1);
	s->setCoord( -x, 1, 2);

	s->setCoord( -y, 2, 0);
	s->setCoord(  x, 2, 1);
	s->setCoord(  0, 2, 2);

	/*        |  x*x  x*y  x*z  |
	*  UUT =  |  y*x  y*y  y*z  |
	*         |  z*x  z*y  z*z  | 
	*/
	Matrix3D *uut = new Matrix3D();
	uut->setCoord( x*x, 0, 0);
	uut->setCoord( x*y, 0, 1);
	uut->setCoord( x*z, 0, 2);

	uut->setCoord( y*x, 1, 0);
	uut->setCoord( y*y, 1, 1);
	uut->setCoord( y*z, 1, 2);

	uut->setCoord( z*x, 2, 0);
	uut->setCoord( z*y, 2, 1);
	uut->setCoord( z*z, 2, 2);

	Matrix3D* ident = Matrix3D::getIdentidade();
	Matrix3D* iuut = ident->subtracao(uut);
	Matrix3D* aiuut = iuut->multiplicacao(cos(a));
	Matrix3D* as = s->multiplicacao(sin(a));
	
	Matrix3D* result = uut->soma( aiuut->soma( as ) ); // M = uut + (cos a)*(I - uut) + (sin a ) * S

	delete s;
	delete uut;
	delete ident;
	delete iuut;
	delete aiuut;
	delete as;

	return result;

}


